#include <iostream>
#include <dirent.h>
#include <fstream>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

#include "CircleQueue.hpp"

int frame_interval = 10;
constexpr char video_file_path[] = "../video/";
constexpr char pics_save_path[] = "../pics/";
constexpr char video_format[] = ".mp4";
constexpr char pic_format[] = ".jpg";

static char show_img(const cv::Mat &img, int interval = 1, const std::string &windowname = "video")
{
    cv::Mat img_ = img.clone();
    cv::putText(img_, "interval: "
                      + std::to_string(interval), cv::Point(7, 22), cv::FONT_HERSHEY_COMPLEX, 0.7, cv::Scalar(255, 255, 255), 1);
    cv::imshow(windowname, img_);
    return char(cv::waitKey(0));
}

static void traverse_folder(std::vector<std::string> &paths, const std::string& video_files_path)
{
    struct dirent *filename;    // return value for readdir()
    DIR *dir;                   // return value for opendir()
    if ((dir = opendir(video_files_path.c_str())) == nullptr)
    {
        std::cerr << "open images folder failed! program exited." << std::endl;
        exit(-1);
    }
    /* read all the paths in the dir */
    while ((filename = readdir(dir)) != nullptr)
    {
        // get rid of "." and ".."
        std::string file = filename->d_name;
        if (file[0] == '.')
            continue;
        if (int(filename->d_type) == 8 && std::string::npos != file.find(video_format))
            paths.emplace_back(video_files_path + file);
        else if (int(filename->d_type) == 4)
            traverse_folder(paths, video_files_path + file + "/");
    }
}

int main(int argc, char **argv)
{
    cv::VideoCapture video;
    std::vector<std::string> paths;
    std::string file;
    std::fstream f;
    int lf = 0, lv = 0;

    if (argc >= 2)
    {
        std::cout << "using cmd model, the params is used to be path to video file." << std::endl
                  << "we do not using record file in this model." << std::endl;
        for (int i = 1; i < argc; ++i)
        {
            file = argv[i];
            if (file.find(video_format) != std::string::npos)
                paths.emplace_back(file);
            else
                std::cerr << "file '" << file << "' is not a valid format file!" << std::endl;
        }
        std::cout << "total " << argc - 1 << " files is used." << std::endl;
    }
    else
    {
        f.open("./last.save", std::ios::in);
        if (!f.is_open())
        {
            std::cout << "record file not find. NOT using last video and frame record."
                      << std::endl;
        }
        else
        {
            std::cout << "find record file. using last video and frame record." << std::endl;
            f >> lv >> lf;
            f.close();
            std::cout << "last video file number is " << lv << ", last frame is " << lf << std::endl;
        }
        traverse_folder(paths, video_file_path);
        paths.erase(paths.begin(), paths.begin() + lv);
//        std::for_each(paths.begin(), paths.end(), [&](auto& p){std::cout << p << std::endl;});
        std::cout << "total " << paths.size() << " files is used." << std::endl;
    }
    if (paths.empty())
    {
        std::cerr << "err. no available file is used. program exited." << std::endl;
        exit(-1);
    }
    int video_num = lv;
    for (auto &path: paths)
    {
        video.open(path);
        if (!video.isOpened())
        {
            std::cerr << "this video cannot be opened. file = " << path << std::endl;
            std::cout << "press any key to continue:" << std::endl;
            getchar();
            continue;
        }
        CircleVector<cv::Mat> queue(100);
        auto p = queue.begin();
        int frame_num = 0;
        try
        {
            while (video_num == lv && frame_num < lf)
            {
                cv::Mat img;
                video >> img;
                //                video >> img;
                ++frame_num;
            }
            while (true)
            {
                if (p == queue.novalue())
                {
                    cv::Mat img;
                    for (int i = 0; i < frame_interval; ++i, ++frame_num)
                    {
                        video >> img;
                        queue.push_back(img.clone());
                        //                    cv::imshow("debug", img);
                        //                    cv::waitKey(0);
                    }
                    p = queue.end();
                }
                file = pics_save_path + std::to_string(video_num) + "-" + std::to_string(frame_num)
                       + pic_format;
                auto ch = show_img(*p, frame_interval, path);
                switch (ch)
                {
                    case 'w':
                        // save an img
                        cv::imwrite(file, *p);
                        std::cout << "file is saved. file = " << file << std::endl;
                        break;
                    case 'q':
                        // recursion
                        for (int i = 0; i < frame_interval; ++i, --frame_num)
                        {
                            p = queue.before(p);
                            if (p == queue.novalue())
                            {
                                std::cerr << "cannot be recursion anymore!" << std::endl;
                                while (cv::waitKey(200) == 'q');
                                p = queue.begin();
                                break;
                            }
                        }
                        break;
                    case 'e':
                        // next img
                        for (int i = 0; i < frame_interval; ++i, ++frame_num)
                        {
                            p = queue.after(p);
                            if (p == queue.novalue())
                                break;
                        }
                        break;
                    case 's':
                        // delete last img in folder
                        if (0 != remove(file.c_str()))
                            std::cerr
                                << "error: remove file failed! please try to remove it manually."
                                << std::endl;
                        else
                            std::cout << "remove file: '" << file << '\'' << std::endl;
                        break;
                    case 'a':
                        // decrease interval
                        --frame_interval;
                        frame_interval = std::max(frame_interval, 1);
                        break;
                    case 'd':
                        // increase interval
                        ++frame_interval;
                        break;
                    case 'z':
                        // save and quit.
                        f.open("./last.save", std::ios::out);
                        f << video_num << " " << frame_num;
                        f.close();
                        std::cout << "record saved successfully in './last.save'. " << std::endl
                                  << "video file number = " << video_num << ", frame = "
                                  << frame_num
                                  << std::endl;
                        exit(0);
                    default:
                        // do nothing
                        break;
                }
            }
        }
        catch (cv::Exception &e)
        {
            std::cout << "this video is end. start next. new video numebr = " << ++video_num
                      << std::endl;
            cv::destroyAllWindows();
        }
    }
    return 0;
}
